//
//  NSObject+KVO.m
//  003-demo
//
//  Created by hzg on 2018/4/26.
//  Copyright © 2018年 tz. All rights reserved.
//

#import "NSObject+KVO.h"
#import <objc/message.h>

@implementation NSObject (KVO)

- (void)gv_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
    
    // 创建一个子类
    NSString* oldName = NSStringFromClass([self class]);
    NSString* newName = [NSString stringWithFormat:@"CustomKVO_%@", oldName];
    
    
   
    Class CustomClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
    objc_registerClassPair(CustomClass);
    
    /// 方法1
//    class_copyIvarList(<#Class  _Nullable __unsafe_unretained cls#>, <#unsigned int * _Nullable outCount#>)
  //  ivar_getTypeEncoding(<#Ivar  _Nonnull v#>)
    
    // 方法2
    NSString* key = [@"_" stringByAppendingString:keyPath];
    Ivar var = class_getInstanceVariable([self class], key.UTF8String);
    const char* type = ivar_getTypeEncoding(var);
    NSLog(@"type = %s", type);
    
    // 修改self属性
    object_setClass(self, CustomClass);

    
    // 重写setter方法
    NSString* methodName = [NSString stringWithFormat:@"set%@:", keyPath.capitalizedString];
    SEL sel = NSSelectorFromString(methodName);
    
    if (strcmp(type, "i") == 0) {
        class_addMethod(CustomClass, sel, (IMP)setterMethodIMP1, "v@:i");
    } else {
        class_addMethod(CustomClass, sel, (IMP)setterMethodIMP, "v@:@");
    }

    
    /// 使用关联属性
    objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
    
}

void setterMethodIMP1(id self, SEL _cmd, int newValue) {
   // NSLog(@"newValue = %@", newValue);
    
    /// 改变父类的属性
    struct objc_super superClass = {self, class_getSuperclass([self class])};
    objc_msgSendSuper(&superClass, _cmd, newValue);
    
    id observer = objc_getAssociatedObject(self, (__bridge const void *)@"objc");
    
    /// 通知观察者，值发生改变
    NSString* methodName = NSStringFromSelector(_cmd);
    NSString* key = getValueKey(methodName);
    NSNumber* value = [NSNumber numberWithInt:newValue];
    objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), key, self, @{key:value}, nil);
}

void setterMethodIMP(id self, SEL _cmd, id newValue) {
    NSLog(@"newValue = %@", newValue);
    
    /// 改变父类的属性
    struct objc_super superClass = {self, class_getSuperclass([self class])};
    objc_msgSendSuper(&superClass, _cmd, newValue);
    
    id observer = objc_getAssociatedObject(self, (__bridge const void *)@"objc");
    
    /// 通知观察者，值发生改变
    NSString* methodName = NSStringFromSelector(_cmd);
    NSString* key = getValueKey(methodName);
    objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), key, self, @{key:newValue}, nil);
}

NSString* getValueKey(NSString* setter) {
    if (setter.length <=0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) {
        return nil;
    }
    
    NSRange range = NSMakeRange(3, setter.length - 4);
    NSString* key = [setter substringWithRange:range];
    
    NSString* fristLetter = [[key substringToIndex:1] lowercaseString];
    key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:fristLetter];
    return key;
}

@end
